using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

namespace PacSnake
{
    /// <summary>
    /// The Board class is our main entry point. It contains the screen
    /// properties, the Render() method to paint all the game elements to the
    /// screen. It handles the key strokes, it implements the game timer and it
    /// controls the flow of the game.
    /// </summary>
    public class FormMain : System.Windows.Forms.Form
    {
        // The game objects
        private Snake snake = new Snake();
        private GameData gameData = new GameData();
        private LevelSet levelSet = new LevelSet();
        private Skin skin = new Skin();
        private Level level;
        private Timer gameTicker;
        private bool showingSplashScreen = true;
        
        // The form objects for drawing to screen
        private PictureBox screen;
        private Bitmap img;
        private Graphics g;
        
        // The font we use for messages to the user
        private Font statusText = new Font("Tahoma", 10, FontStyle.Bold);
        private System.Windows.Forms.MenuItem menuItem8;
        private System.Windows.Forms.MenuItem mnuEnableSounds;
        
        
        // Read current path and remove the 'file:/' from the string
        private string path = Path.GetDirectoryName(System.Reflection.Assembly.
            GetExecutingAssembly().GetName().CodeBase).Substring(6);

        
        #region Constructor
        
        /// <summary>
        /// Constructor: Initialization for drawing to the screen
        /// </summary>
        public FormMain()
        {
            InitializeComponent();
            screen = new PictureBox();
            screen.Size = new System.Drawing.Size(620, 545);
            Controls.Add(screen);
            img = new Bitmap(620, 545);
            g = Graphics.FromImage(img);
            
            mnuStopGame.Enabled = false;

            skin.LoadSkin(gameData.SkinFilename);

            DrawSplashScreen();
        }
        
        #endregion
        
        #region Initializing and stopping the game
        
        /// <summary>
        /// Draws the splash screen
        /// </summary>
        private void DrawSplashScreen()
        {
            g.Clear(Color.Black);
            
            // Get the embedded image from our assembly.
            Assembly thisExe = Assembly.GetExecutingAssembly();
            Stream resourceStream =
                thisExe.GetManifestResourceStream("PacSnake.splash.jpg");
            Image background = Image.FromStream(resourceStream);

            g.DrawImage(background,0 ,0 ,620, 520);
            screen.Image = img;
        }
        
        
        /// <summary>
        /// Initializes the level and the snake. After a key press the games is
        /// ready to start.
        /// </summary>
        private void InitializeGame()
        {
            level = levelSet[gameData.CurrentLevel - 1];
            level.SetOffsets();
            level.GenerateLevel();
            snake.SetSnakeLength(level.NrOfCandy);
            snake.SetStartPosition(level.SnakePosX, level.SnakePosY,
                level.MazeOffsetX, level.MazeOffsetY);

            Render(false);
            DrawMessage("Press a key to start level", 220, 524);
            gameData.GameStatus = GameStatus.WaitingForKey;
        }
        
        
        /// <summary>
        /// Stops the current game when it's running
        /// </summary>
        private void StopGame()
        {
            mnuNewGame.Enabled = true;
            mnuStopGame.Enabled = false;
            mnuSkin.Enabled = true;
            gameTicker.Enabled = false;
            gameData.GameStatus = GameStatus.NotStarted;
            showingSplashScreen = true;
        }
        
        #endregion

        #region Drawing stuff to screen
        
        /// <summary>
        /// The Render() method redraws the screen after each tick of the
        /// timer. It uses a back buffer to prevent the screen from flickering
        /// each time it's repainted. We use a levelOnly parameter to decide if
        /// we want to redraw the whole screen or just the level without the
        /// status bar. If we select a different skin when there's a message at
        /// the bottom, the message would get erased.
        /// <param name="levelOnly">Check if we redraw the whole screen or just
        /// the level</param>
        /// </summary>
        private void Render(bool levelOnly)
        {
            if (!levelOnly)
                g.Clear(Color.Black);
            
            // Draw level
            g.DrawImage(level.Draw(620, 520, skin), 0, 0);

            // Draws the body, tail and head of the snake
            for (int i = 1; i < snake.Length - 1; i++) 
                g.DrawImage(snake.GetBodyImage(i), snake.Body[i].X,
                    snake.Body[i].Y);
            g.DrawImage(snake.GetTailImage(),snake.Body[snake.Length - 1].X,
                snake.Body[snake.Length - 1].Y);
            g.DrawImage(snake.GetHeadImage(), snake.Body[0].X,snake.Body[0].Y);
            
            // Draw level information at bottom of screen
            g.DrawString("Level:", statusText, Brushes.White, 10, 524);
            g.DrawString(gameData.CurrentLevel.ToString(), statusText,
                Brushes.Orange, 55, 524);

            screen.Image = img;
        }
        

        /// <summary>
        /// Draws a user message to the screen
        /// </summary>
        /// <param name="msg">Message</param>
        /// <param name="x">X-coordinate of the message</param>
        /// <param name="y">Y-coordinate of the message</param>
        private void DrawMessage(string msg, int x, int y) 
        {
            g.DrawString(msg, statusText, Brushes.White, x, y);
            screen.Image = img;
        }

        #endregion

        #region Pressing a key
        
        /// <summary>
        /// Handles the key presses. When the game is running it controls the
        /// snake. When the game is not running it controls the other keys to
        /// start the game, etc...
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AKeyDown(object sender, KeyEventArgs e) 
        {
            switch (gameData.GameStatus)
            {
                // If the game is in progress, we control the snake
                case GameStatus.InProgress:
                    switch (e.KeyData.ToString()) 
                    {
                        case "Left":
                            snake.DesiredDirection = MoveDirection.Left;
                            break;
                        case "Right":
                            snake.DesiredDirection = MoveDirection.Right;
                            break;
                        case "Up":
                            snake.DesiredDirection = MoveDirection.Up;
                            break;
                        case "Down":
                            snake.DesiredDirection = MoveDirection.Down;
                            break;
                        default:
                            // Don't respond on other key presses
                            break;
                    }
                    break;
                // If the snake has died we press a random key to restart
                case GameStatus.SnakeDied:
                    InitializeGame();
                    break;
                // If the level is finished we press a key to start next
                case GameStatus.LevelFinished:
                    // If this wasn't the last level, start next level
                    if (gameData.CurrentLevel < levelSet.NrOfLevelsInSet)
                    {
                        gameData.CurrentLevel++;
                        level = levelSet[gameData.CurrentLevel - 1];
                        InitializeGame();
                    }
                    else
                        gameData.GameStatus = GameStatus.WaitingForKey;
                    
                    break;
                // Level is ready to start, let's wait for a key
                case GameStatus.WaitingForKey:
                    gameData.GameStatus = GameStatus.InProgress;
                    gameTicker.Interval = level.Speed;
                    gameTicker.Enabled = true;
                    mnuSkin.Enabled = false;
                    break;
            }
        }
        
        #endregion

        #region Timer method
        
        /// <summary>
        /// The timer checks the status of the game
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnTimer(object sender, EventArgs e) 
        {
            // Poor snake has died, so stop timer and wait for restart
            if (gameData.GameStatus == GameStatus.SnakeDied)
            {
                gameTicker.Enabled = false;
                DrawMessage(
                    "Snake died! Press a key to restart level", 185, 524);
            }
            // Level has finished
            else if (gameData.GameStatus != GameStatus.InProgress)
            {
                gameTicker.Enabled = false;
                mnuSkin.Enabled = true;
                
                levelSet.SetLevelFinished(gameData.CurrentLevel);

                if (gameData.CurrentLevel < levelSet.NrOfLevelsInSet)
                {
                    DrawMessage("Level finsished! Press a key to start next " +
                        "level", 163, 524);
                    gameData.GameStatus = GameStatus.LevelFinished;
                }
                else
                {
                    DrawMessage("All levels done. Congratulations!", 200, 524);
                    StopGame();
                }
            } 
            else 
            {
                // The game is in progress so let's check for move directions
                // and update snake position
                snake.checkDirections(level);
                snake.MoveBody();
                
                // Check if snake has crashed into itself
                if (snake.CheckCollision())
                    gameData.GameStatus = GameStatus.SnakeDied;
            
                // Check if the snake ate some food
                if (level.CheckFood(snake.Body[0]))
                {
                    snake.Grow(1);
                    if (gameData.SoundsEnabled)
                        WinMM.PlayWavResource("eat.wav");
                }
                else
                    if (gameData.SoundsEnabled)
                        WinMM.PlayWavResource("move.wav");

                // Stop timer when all food is eaten and level is done
                if (level.NrOfCandy == 0)
                    gameData.GameStatus = GameStatus.LevelFinished;

                // Repaints screen
                Render(false);
            }
        }
        
        #endregion

        #region Menu Item Click Handlers
        
        /// <summary>
        /// Starts a new game by first selecting a level set
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuNewGame_Click(object sender, System.EventArgs e)
        {
            FormLevelSet formLevelSet = new FormLevelSet();
            formLevelSet.ShowDialog();
            
            if (formLevelSet.OkButtonPressed)
            {
                levelSet.LoadLevelSetInfo(formLevelSet.LevelSetInfo.Filename);
                levelSet.LoadLevels(formLevelSet.LevelSetInfo.Filename);
                
                if (formLevelSet.ContinueLastPlayedLevel)
                {
                    if (levelSet.LastFinishedLevelInSet ==
                        levelSet.NrOfLevelsInSet)
                        gameData.CurrentLevel =
                            levelSet.LastFinishedLevelInSet;
                    else
                        gameData.CurrentLevel =
                            levelSet.LastFinishedLevelInSet + 1;
                }
                else
                    gameData.CurrentLevel = 1;

                level = levelSet[gameData.CurrentLevel - 1];
                
                mnuNewGame.Enabled = false;
                mnuStopGame.Enabled = true;
                showingSplashScreen = false;
                InitializeGame();
            }
        }
        
        
        /// <summary>
        /// Stops a game when it's in progress
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuStopGame_Click(object sender, System.EventArgs e)
        {
            StopGame();
            DrawSplashScreen();
        }

        
        /// <summary>
        /// Lets the user select a different skin
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuSkin_Click(object sender, System.EventArgs e)
        {
            FormSkins formSkins = new FormSkins(gameData.SkinFilename);
            formSkins.ShowDialog();
            
            if (formSkins.OkButtonPressed)
            {
                skin.LoadSkin(formSkins.Skin.Filename);
                gameData.SetSkin(skin.Filename);
                
                if (!showingSplashScreen)
                    Render(true);
            }
        }
        
        
        
        private void mnuEnableSounds_Click(object sender, System.EventArgs e)
        {
            if (mnuEnableSounds.Checked)
            {
                gameData.SoundsEnabled = false;
                mnuEnableSounds.Checked = false;
            }
            else
            {
                gameData.SoundsEnabled = true;
                mnuEnableSounds.Checked = true;
            }
        }
        
        
        /// <summary>
        /// Closes the application.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuExit_Click(object sender, System.EventArgs e)
        {
            this.Close();
        }
        
        
        /// <summary>
        /// Shows the about box
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuAbout_Click(object sender, System.EventArgs e)
        {
            FormAbout formAbout = new FormAbout();
            formAbout.ShowDialog();
        }
        
        
        /// <summary>
        /// Opens the Sokoban Pro website in a browser window
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuWebsite_Click(object sender, System.EventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start(
                    "http://www.jabbah.nl/pacsnake");
            }
            catch
            {
                MessageBox.Show("Error launching webbrowser.\n\r\n\r" +
                    "Visit http://www.jabbah.nl/pacsnake to visit the " +
                    "PacSnake website.", "Whoops!",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        
        #endregion

        #region Windows Form Designer generated code

        private System.ComponentModel.IContainer components;
        
        private System.Windows.Forms.MainMenu mainMenu1;
        private System.Windows.Forms.MenuItem menuItem1;
        private System.Windows.Forms.MenuItem menuItem4;
        private System.Windows.Forms.MenuItem menuItem5;
        private System.Windows.Forms.MenuItem mnuWebsite;
        private System.Windows.Forms.MenuItem mnuAbout;
        private System.Windows.Forms.MenuItem mnuNewGame;
        private System.Windows.Forms.MenuItem mnuExit;
        private System.Windows.Forms.MenuItem menuItem6;
        private System.Windows.Forms.MenuItem mnuStopGame;
        private System.Windows.Forms.MenuItem menuItem3;
        private System.Windows.Forms.MenuItem menuItem7;
        private System.Windows.Forms.MenuItem mnuSkin;
        
        private void InitializeComponent() 
        {
            this.components = new System.ComponentModel.Container();
            System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(FormMain));
            this.gameTicker = new System.Windows.Forms.Timer(this.components);
            this.mainMenu1 = new System.Windows.Forms.MainMenu();
            this.menuItem1 = new System.Windows.Forms.MenuItem();
            this.mnuNewGame = new System.Windows.Forms.MenuItem();
            this.menuItem4 = new System.Windows.Forms.MenuItem();
            this.mnuStopGame = new System.Windows.Forms.MenuItem();
            this.menuItem7 = new System.Windows.Forms.MenuItem();
            this.mnuSkin = new System.Windows.Forms.MenuItem();
            this.menuItem3 = new System.Windows.Forms.MenuItem();
            this.mnuExit = new System.Windows.Forms.MenuItem();
            this.menuItem5 = new System.Windows.Forms.MenuItem();
            this.mnuAbout = new System.Windows.Forms.MenuItem();
            this.menuItem6 = new System.Windows.Forms.MenuItem();
            this.mnuWebsite = new System.Windows.Forms.MenuItem();
            this.mnuEnableSounds = new System.Windows.Forms.MenuItem();
            this.menuItem8 = new System.Windows.Forms.MenuItem();
            // 
            // gameTicker
            // 
            this.gameTicker.Tick += new System.EventHandler(this.OnTimer);
            // 
            // mainMenu1
            // 
            this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                                      this.menuItem1,
                                                                                      this.menuItem5});
            // 
            // menuItem1
            // 
            this.menuItem1.Index = 0;
            this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                                      this.mnuNewGame,
                                                                                      this.menuItem4,
                                                                                      this.mnuStopGame,
                                                                                      this.menuItem7,
                                                                                      this.mnuSkin,
                                                                                      this.menuItem8,
                                                                                      this.mnuEnableSounds,
                                                                                      this.menuItem3,
                                                                                      this.mnuExit});
            this.menuItem1.Text = "Game";
            // 
            // mnuNewGame
            // 
            this.mnuNewGame.Index = 0;
            this.mnuNewGame.Text = "&New Game...";
            this.mnuNewGame.Click += new System.EventHandler(this.mnuNewGame_Click);
            // 
            // menuItem4
            // 
            this.menuItem4.Index = 1;
            this.menuItem4.Text = "-";
            // 
            // mnuStopGame
            // 
            this.mnuStopGame.Index = 2;
            this.mnuStopGame.Text = "&Stop Game";
            this.mnuStopGame.Click += new System.EventHandler(this.mnuStopGame_Click);
            // 
            // menuItem7
            // 
            this.menuItem7.Index = 3;
            this.menuItem7.Text = "-";
            // 
            // mnuSkin
            // 
            this.mnuSkin.Index = 4;
            this.mnuSkin.Text = "&Choose Skin...";
            this.mnuSkin.Click += new System.EventHandler(this.mnuSkin_Click);
            // 
            // menuItem3
            // 
            this.menuItem3.Index = 7;
            this.menuItem3.Text = "-";
            // 
            // mnuExit
            // 
            this.mnuExit.Index = 8;
            this.mnuExit.Text = "E&xit";
            this.mnuExit.Click += new System.EventHandler(this.mnuExit_Click);
            // 
            // menuItem5
            // 
            this.menuItem5.Index = 1;
            this.menuItem5.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                                      this.mnuAbout,
                                                                                      this.menuItem6,
                                                                                      this.mnuWebsite});
            this.menuItem5.Text = "Help";
            // 
            // mnuAbout
            // 
            this.mnuAbout.Index = 0;
            this.mnuAbout.Text = "&About...";
            this.mnuAbout.Click += new System.EventHandler(this.mnuAbout_Click);
            // 
            // menuItem6
            // 
            this.menuItem6.Index = 1;
            this.menuItem6.Text = "-";
            // 
            // mnuWebsite
            // 
            this.mnuWebsite.Index = 2;
            this.mnuWebsite.Text = "PacSnake &Website...";
            this.mnuWebsite.Click += new System.EventHandler(this.mnuWebsite_Click);
            // 
            // mnuEnableSounds
            // 
            this.mnuEnableSounds.Checked = true;
            this.mnuEnableSounds.Index = 6;
            this.mnuEnableSounds.Text = "Sounds Enabled";
            this.mnuEnableSounds.Click += new System.EventHandler(this.mnuEnableSounds_Click);
            // 
            // menuItem8
            // 
            this.menuItem8.Index = 5;
            this.menuItem8.Text = "-";
            // 
            // FormMain
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.BackColor = System.Drawing.Color.Black;
            this.ClientSize = new System.Drawing.Size(620, 545);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
            this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
            this.Menu = this.mainMenu1;
            this.Name = "FormMain";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "PacSnake";
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.AKeyDown);

        }
        
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null) 
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }
        
        // Our main entry
        [STAThread]
        static void Main() 
        {
            Application.Run(new FormMain());
        }
        
        #endregion
    }
}
